<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>框选合并功能</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    .header {
      height: 100px;
      display: flex;
      align-items: center;
      justify-content: center;
    }

    .box {
      padding: 50px;
      background-color: bisque;
    }

    .table {
      width: 900px;
      /* margin: auto;
      margin-top: 20px; */
      /* display: table; */
      border-collapse: collapse;
    }

    .tr {
      display: table-row;
    }

    .td {
      display: table-cell;
      width: 100px;
      height: 100px;
      text-align: center;
      line-height: 100px;
      border: 0.5px solid #000;
    }

    /* 被选中时的样式 */
    .seled {
      border: 0.5px solid red;
      background-color: #D6DFF7;
    }

    .ul{
      margin-left: 20px;
    }
  </style>
</head>

<body>
  <!-- <div class="table">
    <div class="tr">
      <div class="td">1</div>
      <div class="td">2</div>
      <div class="td">3</div>
      <div class="td">4</div>
      <div class="td">5</div>
      <div class="td">6</div>
    </div>
    <div class="tr">
      <div class="td">7</div>
      <div class="td">8</div>
      <div class="td">9</div>
      <div class="td">10</div>
      <div class="td">11</div>
      <div class="td">12</div>
    </div>
  </div> -->

  <div id="app"></div>

  <template id="tem">
    <table class="table">
      <tr v-for="tr in tableArr">
        <td class="td" v-for="td in tr" :index="td.id" :rowspan="td.rowspan" :colspan="td.colspan">{{ td.id }}</td>
      </tr>
    </table>

    <div class="header">
      <button @click="mergeCell">合并单元格</button>
    </div>

    <!-- <hr> -->
    <!-- <h2>待完善的问题：</h2> -->
    <div>
      <ul class="ul">
        <!-- <li>已合并单元格，不能再次合并</li> -->
      </ul>
    </div>
  </template>
</body>


<script src="./js/vue.js"></script>
<script>
  const app = Vue.createApp({
    template: '#tem',
    data() {
      return {
        tableArr: [
          [
            { id: 1.1, rowspan: 1, colspan: 1 },
            { id: 1.2, rowspan: 1, colspan: 1 },
            { id: 1.3, rowspan: 1, colspan: 1 },
            { id: 1.4, rowspan: 1, colspan: 1 },
            { id: 1.5, rowspan: 1, colspan: 1 },
            { id: 1.6, rowspan: 1, colspan: 1 },
            { id: 1.7, rowspan: 1, colspan: 1 },
            { id: 1.8, rowspan: 1, colspan: 1 },
            { id: 1.9, rowspan: 1, colspan: 1 },
          ],
          [
            { id: 2.1, rowspan: 1, colspan: 1 },
            { id: 2.2, rowspan: 1, colspan: 1 },
            { id: 2.3, rowspan: 1, colspan: 1 },
            { id: 2.4, rowspan: 1, colspan: 1 },
            { id: 2.5, rowspan: 1, colspan: 1 },
            { id: 2.6, rowspan: 1, colspan: 1 },
            { id: 2.7, rowspan: 1, colspan: 1 },
            { id: 2.8, rowspan: 1, colspan: 1 },
            { id: 2.9, rowspan: 1, colspan: 1 },
          ],
          [
            { id: 3.1, rowspan: 1, colspan: 1 },
            { id: 3.2, rowspan: 1, colspan: 1 },
            { id: 3.3, rowspan: 1, colspan: 1 },
            { id: 3.4, rowspan: 1, colspan: 1 },
            { id: 3.5, rowspan: 1, colspan: 1 },
            { id: 3.6, rowspan: 1, colspan: 1 },
            { id: 3.7, rowspan: 1, colspan: 1 },
            { id: 3.8, rowspan: 1, colspan: 1 },
            { id: 3.9, rowspan: 1, colspan: 1 },
          ],
          [
            { id: 4.1, rowspan: 1, colspan: 1 },
            { id: 4.2, rowspan: 1, colspan: 1 },
            { id: 4.3, rowspan: 1, colspan: 1 },
            { id: 4.4, rowspan: 1, colspan: 1 },
            { id: 4.5, rowspan: 1, colspan: 1 },
            { id: 4.6, rowspan: 1, colspan: 1 },
            { id: 4.7, rowspan: 1, colspan: 1 },
            { id: 4.8, rowspan: 1, colspan: 1 },
            { id: 4.9, rowspan: 1, colspan: 1 },
          ],
        ]
      }
    },
    methods: {
      mergeCell() {
        console.log('merge', selectedNode, attrArr);
        // 移除选中的单元格
        attrArr.forEach(id => {
          let tr = this.tableArr[id.split('.')[0] - 1];
          this.tableArr[id.split('.')[0] - 1] = tr.filter(td => td.id != id)
        })
        console.log('合并后：', this.tableArr);
        // 计算合并单元格的大小，以及位置信息
        let insertTrNum = attrArr[0].split('.')[0] - 1;
        let insertTdNum = attrArr[0].split('.')[1] - 1;
        let firstTdIndex = attrArr[0];
        let endTdIndex = attrArr[attrArr.length - 1];
        let rowspan = endTdIndex.split('.')[0] - firstTdIndex.split('.')[0] + 1;
        let colspan = endTdIndex.split('.')[1] - firstTdIndex.split('.')[1] + 1;
        console.log('插入的位置：', insertTrNum, insertTdNum);
        console.log('rowspan', rowspan);
        console.log('colspan', colspan);
        // 添加一个合并好的单元格到指定的位置
        this.tableArr[insertTrNum].splice(insertTdNum, 0, { id: attrArr[0], rowspan, colspan })

        // 合并后，初始化
        selectedNode = [];
        attrArr = [];
        document.querySelectorAll(".td").forEach(td => td.className = 'td')
      }
    },
  })
  app.mount("#app");
</script>
<script type="text/javascript">
  // 被选中的节点元素
  let selectedNode = [];
  let attrArr = [];
  /*
   * @description:
   * wrapper: 框选容器的class
   * targetClassName：可被框选元素的class
   * selectedClassName: 被框选时的样式（类名）
   */
  (function (wrapper, targetClassName, selectedClassName) {
    let Box = document.querySelector(wrapper);
    // 监听鼠标按下事件
    Box.onmousedown = function () {
      // var selList = [];
      // var fileNodes = document.getElementsByTagName("div");

      // 获取要被框选的元素集合
      // for (var i = 0; i < fileNodes.length; i++) {
      //   if (fileNodes[i].className.indexOf(targetClassName) != -1) {
      //     fileNodes[i].className = targetClassName;
      //     selList.push(fileNodes[i]);
      //   }
      // }

      var selList = document.querySelectorAll("." + targetClassName);

      // 标识是否开始框选
      var isSelect = true;
      var evt = window.event || arguments[0];
      // 获取鼠标按下时的位置
      var startX = (evt.x || evt.clientX);
      var startY = (evt.y || evt.clientY);
      // 创建框选div
      var selDiv = document.createElement("div");
      selDiv.style.cssText = "position:absolute;width:0px;height:0px;font-size:0px;margin:0px;padding:0px;border:1px dashed #0099FF;background-color:#C3D5ED;z-index:1000;filter:alpha(opacity:60);opacity:0.6;display:none;";
      selDiv.id = "selectDiv";
      document.body.appendChild(selDiv);
      selDiv.style.left = startX + "px";
      selDiv.style.top = startY + "px";
      // 鼠标移动时的位置坐标
      var _x = null;
      var _y = null;
      // 阻止冒泡、默认行为
      clearEventBubble(evt);

      // 监听鼠标移动事件
      Box.onmousemove = function () {
        evt = window.event || arguments[0];
        if (isSelect) {
          if (selDiv.style.display == "none") {
            selDiv.style.display = "";
          }
          _x = (evt.x || evt.clientX);
          _y = (evt.y || evt.clientY);

          // 重绘选择框的大小
          selDiv.style.left = Math.min(_x, startX) + "px";
          selDiv.style.top = Math.min(_y, startY) + "px";
          selDiv.style.width = Math.abs(_x - startX) + "px";
          selDiv.style.height = Math.abs(_y - startY) + "px";

          // ---------------- �ؼ��㷨 ---------------------  
          var _l = selDiv.offsetLeft, _t = selDiv.offsetTop;
          var _w = selDiv.offsetWidth, _h = selDiv.offsetHeight;
          // console.log('_l', _l);
          // console.log('_w', _w);
          for (var i = 0; i < selList.length; i++) {
            var sl = selList[i].offsetWidth + selList[i].offsetLeft;
            var st = selList[i].offsetHeight + selList[i].offsetTop;
            // console.log('sl', i, sl);
            // console.log('st', i, st);
            if (sl > _l && st > _t && selList[i].offsetLeft < _l + _w && selList[i].offsetTop < _t + _h) {
              if (selList[i].className.indexOf(selectedClassName) == -1) {
                selList[i].className = selList[i].className + ` ${selectedClassName}`;
                console.log(selList[i].getAttribute("index"));
              }
            } else {
              if (selList[i].className.indexOf(selectedClassName) != -1) {
                selList[i].className = targetClassName;
              }
            }
          }
        }
        clearEventBubble(evt);
      }

      // 监听鼠标抬起事件
      document.onmouseup = function () {
        isSelect = false;
        if (selDiv) {
          document.body.removeChild(selDiv);
          // 展示结果
          showSelDiv(selList);
        }
        selList = null, _x = null, _y = null, selDiv = null, startX = null, startY = null, evt = null;
      }
    }

    // 阻止冒泡和默认行为
    function clearEventBubble(evt) {
      if (evt.stopPropagation)
        evt.stopPropagation();
      else
        evt.cancelBubble = true;
      if (evt.preventDefault)
        evt.preventDefault();
      else
        evt.returnValue = false;
    }

    // 处理框选结果
    function showSelDiv(arr) {
      var count = 0;
      var selInfo = "";
      for (var i = 0; i < arr.length; i++) {
        if (arr[i].className.indexOf("seled") != -1) {
          selectedNode.push(arr[i])
          attrArr.push(arr[i].getAttribute('index'))
          count++;
          selInfo += arr[i].innerHTML + "\n";
        }
      }
      // alert("共选择" + count + "个文件，分别是：\n" + selInfo);
      console.log("被选中的节点元素：", selectedNode);
      console.log("被选中的节点元素的位置索引：", attrArr);
    }
  })(".table", 'td', 'seled');

  // let Btn = document.getElementById("btn");

  // Btn.onclick = function () {
  //   if (selectedNode.length) {
  //     // 移除选中的元素
  //     selectedNode.forEach((item, index) => {
  //       let pNode = item.parentNode;
  //       pNode.removeChild(item);
  //     });

  //     // 添加一个合并好的单元格元素
  //     let addCel = document.createElement('td');
  //     addCel.className = 'td';
  //     // 计算合并的行数
  //     let rowspanNum = Math.ceil(attrArr[attrArr.length - 1] - attrArr[0] + .1);
  //     addCel.setAttribute('rowspan', rowspanNum)
  //     // 计算合并的列数
  //     addCel.setAttribute('colspan', attrArr.length / rowspanNum);
  //     addCel.setAttribute('index', attrArr[0]);
  //     addCel.innerHTML = '合并后的单元格';
  //     console.log(addCel);
  //     // 将合并后的单元格添加到指定位置
  //     let currentNode = document.querySelector(`td[index='${attrArr[0] - 0.1}']`);
  //     // 下一个兄弟节点
  //     let nextNode = currentNode.nextElementSibling;
  //     // 获取父节点
  //     let curTr = currentNode.parentNode;
  //     console.log('currentNode', currentNode);
  //     console.log('nextNode', nextNode);
  //     console.log('curTr', curTr);
  //     curTr.insertBefore(addCel, nextNode)


  //     selectedNode = [];
  //     attrArr = [];
  //   } else {
  //     alert("请先框选单元格！")
  //   }
  // }
</script>


</html>